home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 48 / Amiga Format CD48 (1999-12-13)(Future Publishing)(GB)(Track 1 of 2)[!][issue 2000-01].iso / -in_the_mag- / networking / crosspc / parpc04 / hardware / src / parkern.old < prev    next >
Text File  |  1993-08-31  |  32KB  |  1,081 lines

  1. ; Lowlevel routines for PARnet
  2. ; 03-JUN-93 First PC version <S.A.Pechler@bdk.tue.nl>
  3. ; 03-JUL-93 Fixed bug when transferring odd number of bytes.
  4. ;           Optimized by putting some counters in registers   <S.A.Pechler>
  5. ; 07-JUL-93 Bugfix: ES was not set when reading into overflow buffer.
  6. ;           Deleted leading underscores from all data labels.
  7. ;           Added ParReadV and ParWriteV functions.           <S.A.Pechler>
  8. ; 11-AUG-93 Bugfix: When an error occured in parread(), the errornumber was
  9. ;        set in the wrong register (was BX, had to be AX). <S.A.Pechler>
  10. ; 01-SEP-93 Optimized the functions by using block move operations (STOSB).
  11. ;           Changed the register usage of read_data() and write_data().
  12. ;
  13. ;
  14. ;   PARALLEL PORT NETWORK LOW LEVEL ROUTINES
  15. ;
  16. ;  THE CABLE:  Connect D7-D0,SEL,POUT, and BUSY across,
  17. ;           Connect ACK to SEL both locally and across,
  18. ;           Connect GND lines indicated:
  19. ;
  20. ;   (2-9)   D7-D0   ------------    D7-D0  (data)
  21. ;   (12)    POUT    ------------    POUT   (~req)
  22. ;   (11)    BUSY    ------------    BUSY   (~ack)  PARNET INTERFACE
  23. ;   (13)    SEL     --+------+--    SEL    ( ctl)
  24. ;   (10)    ACK     -/        \-    ACK    ( irq)
  25. ;   (18-22) GND     ------------    GND
  26. ;
  27. ;
  28. ;   The interface has in it's standard layout no pullup resistors
  29. ;   on the lines. You don't need them when at least one Amiga is
  30. ;   connected on the line. When connecting PC's only, you need to
  31. ;   place 1 array of pullup resistors in the interface of the LAST
  32. ;   machine on the line. Such a pullup is made by connecting all the
  33. ;   lines shown in the cable above (except for GND) with 4.7 KOhm
  34. ;   resistors to +5 Volt.
  35. ;
  36. ;      +--+        +-----+          +--+           +--+
  37. ;      |PC|        |Amiga|          |PC|           |PC|
  38. ;      +--+        +-----+          +--+           +--+
  39. ;        |            |               |              |
  40. ;     +-----+         |            +-----+        +-----+    
  41. ;     |Iface|   .     |            |Iface|        |Iface|    
  42. ;     +-----+   .     |            +-----+        +-----+
  43. ;        |      |     |               |       .      | ++-+
  44. ;        \------+-----/               |       .      +-|PR|
  45. ;  PARnet cable between PC & Amiga    |       |      | +--+
  46. ;       (no resistors needed)         \-------+------/
  47. ;                                   PARnet cable between PC's
  48. ;                                   (add pullup resistors)
  49. ;
  50. ;   More than 2 machines on the same line are ofcourse possible.
  51. ;   Note that you can't connect more than 3 Amiga's (or 2 Amiga's
  52. ;   and 1 PC) without additional buffers, otherwise you'll blow up
  53. ;   your CIA's. The software can handle 254 machines.
  54. ;
  55. ;   Parallel port usage:
  56. ;
  57. ;   DATA PORT   : used for data output only
  58. ;   STATUS PORT : used for data & control input
  59. ;   CONTROL PORT: used for control output
  60. ;
  61. ;   All lines pulled up.  Thus, asserted state is a 0.    Idle
  62. ;   state is an undriven (1).  Protocol transfers a byte at
  63. ;   a time.  Protocol is ethernet style with a small window
  64. ;   of error in the line aquisition routine.
  65. ;
  66. ;   Note:   Timeouts should be set around a second.  Ideally
  67. ;        defaults should be fixed on faster machines.
  68. ;
  69. ;   LINE description:
  70. ;
  71. ;   PARnet: Input: Output:  Description:
  72. ;   ---------------------------------------------------------
  73. ;   ~ACK    SLCT   STROBE   hand shake
  74. ;   ~REQ    POUT   AUTOLF   hand shake
  75. ;    CTL    ACK    INIT     1 = special byte, 0 = data byte.
  76. ;                (for address mark, valid when ~REQ
  77. ;                 goes low.    For EOP mark, sample
  78. ;                 when ~REQ goes high)
  79. ;
  80. ;   See the associated routines for the exact low-level protocols.
  81. ;
  82. ;
  83. ;   PROTOCOL DESCRIPTION:
  84. ;
  85. ;   HandShake    (Reader <- Writer transfer).  A Handshake
  86. ;        sequence transfers TWO bytes of information.
  87. ;
  88. ;        WRITER                READER
  89. ;    |-> place data, ~REQ->0
  90. ;    |                 wait for ~REQ->0
  91. ;    |                 read data & store
  92. ;    |                 set ~ACK->0
  93. ;    |   wait ~ACK->0
  94. ;    |   place data, ~REQ->1
  95. ;    |                 wait for ~REQ->1
  96. ;    |                 read data & store
  97. ;    |                 set ~ACK->1
  98. ;    |   wait ~ACK->1
  99. ;    |<- LOOP  (2 bytes written)      LOOP  (2 bytes read)
  100. ;
  101. ;
  102. ;   Read:   (1) Determine if your machine is being addressed
  103. ;        (~REQ=0, data=myaddress, CTL=1)
  104. ;        (1) set ~ACK to output and
  105. ;        (2) Handshake sequence for the address mark, only
  106. ;        first byte valid.
  107. ;        (3) Handshake sequence for data util rcv byte
  108. ;        with CTL = 1 (EOP), byte must == 0.
  109. ;        (4) Set ~ACK to input
  110. ;
  111. ;   Write:  (1) AQUIRE THE NETWORK (see below)
  112. ;        involves gaining control and then setting the
  113. ;        CTL and ~REQ to outputs.
  114. ;
  115. ;        Also checks if somebody is writing to us,
  116. ;        in which case -2 is returned instantaniously
  117. ;        indicating we should do a ParRead().
  118. ;
  119. ;        (2) Handshake sequence for address mark, send dest
  120. ;        address (second byte garbage).  Note that CTL->1
  121. ;        *BEFORE* we set ~REQ->0
  122. ;        (3) Handshake sequence for data bytes
  123. ;        (4) Handshake sequence for EOP mark (Note that CTL->1
  124. ;        *AFTER* we get the ~ACK->1 and before release
  125. ;        ~REQ (->1).  Only firstbyte valid and set to 0.
  126. ;
  127. ;        (5) Set ~ACK and ~CTL to inputs
  128. ;
  129. ;   AQUIRE: Line aquisition prevents two people from writing to
  130. ;        the net at the same time.
  131. ;
  132. ;        * A line is considered aquired if ANY of the 3
  133. ;          control lines is 0.
  134. ;        * If network is not aquired:
  135. ;        - set ~ACK to output and 1
  136. ;        - bclr ~ACK to 0 and bne success
  137. ;          (else set ~ACK to input and try again)
  138. ;        - set data lines to output and place my address
  139. ;          (Must ]be done before CTL is glitched)
  140. ;        - set ~CTL to output and 1
  141. ;        - set CTL to 0 and then 1 (glitch it) to cause
  142. ;          FLAG interrupt on all other machines
  143. ;        - set ~REQ to output and 0 (beginning of handshake
  144. ;          sequence)
  145. ;        - lastly, release ~ACK by setting it to an input
  146. ;
  147. ;          Note that at all times at least one line is a 0
  148. ;          so no other machine will attempt to aquire the net.
  149. ;
  150. ;   Note that the destination address is placed on the data
  151. ;   lines after we have aquired the line but before we glitch
  152. ;   the CTL line to cause an interrupt.  This allows the
  153. ;   other machines to instantaniously determine who is being
  154. ;   addressed.
  155. ;
  156. ;   Note that the CTL line gets glitched at the end of a packet
  157. ;   too for the EOP mark.  In this case there is a 0 on the
  158. ;   data lines so while an interrupt is generated, nobody
  159. ;   thinks they are being addressed.
  160. ;
  161. ;
  162. ;   ASSEMBLING/COMPILING the code:
  163. ;
  164. ;   When assembling, use the following flags (turbo-assembler V2.0):
  165. ;
  166. ;   tasm -mx -t -DMEMMOD=large parkern.asm
  167. ;   option -zi adds full debug information.
  168. ;
  169. ;   When including the routines in C, use the following prototypes:
  170. ;
  171. ;   typedef unsigned short int16;
  172. ;   extern void paraddress(int16,int16);
  173. ;   extern int pardataready(void);
  174. ;   extern int parreadV();
  175. ;   extern int parread(unsigned char *,int16);
  176. ;   extern int parwriteV();
  177. ;   extern int parwrite(int16, unsigned char *,int16);
  178. ;
  179.  
  180.     .MODEL MEMMOD,C
  181.     LOCALS
  182.     %MACS
  183.     .LALL
  184.  
  185.     .DATA
  186. ParNetAddr    db 1        ; my PARnet address (default)
  187. LPTData        dw 0378h    ; LPT data port address (default)
  188. LPTStatus    dw 0379h    ; LPT status port address (default)
  189. LPTControl    dw 037ah    ; LPT control port address (default)
  190.  
  191. ParLLTimeout    dw 65535
  192.     ;should be dl 983025    ; default timeout value (about 1 second?)
  193. DestAddress    db 5        ; Destination address for write
  194. Dummybuf    db 0        ; dummy buffer
  195.         db 0        ; dummy buffer
  196.         db 0,0        ; padding
  197. NextRVector     db 0            ; Flag: vector to next read buffer present
  198. NextWVector    db 0        ; Flag: vector to next write buffer present
  199.  
  200.     .CODE
  201.         PUBLIC paraddress,pardataready,parreadV,parread,parwrite,parwriteV
  202.  
  203. ; Parnet interface truth table
  204. ;
  205. ; 0 = line is 0V
  206. ; 1 = line is 5V
  207. ; X = don't care
  208. ;
  209. ;SELIN  D3 STROBE | RD RC WD WC | Function
  210. ;-----------------+-------------+---------------
  211. ; 0      0    X   | 0  1  1  1    | Read Data
  212. ; 0      1    X   | 1  1  1  0    | Write Control
  213. ; 1      X    0   | 1  1  0  1    | Write Data
  214. ; 1      X    1   | 1  0  1  1    | Read Control
  215. ;
  216.  
  217. ; Put the interface in a stable mode by switching it into
  218. ; 'Read Control' mode.
  219. ;
  220. stable: push ax
  221.     push dx
  222.     mov dx,LPTControl    ;control register 2 (to clock in data & Read Control)
  223.  
  224.     mov al,14h    ;all outputs to 1 (INIT is not inverted!), enable IRQ7
  225.     out dx,al    ;STROBE=1 & SELIN=1 -> read control (stable mode).
  226.     pop dx
  227.     pop ax
  228.     ret
  229.  
  230. ; Set the datalines to 'input' by setting all lines to high.
  231. ; (Open collector outputs not active).
  232. ;
  233. data_input:
  234.     push ax
  235.     push dx
  236.     mov dx,LPTData    ;data register
  237.     mov al,0ffh    ;datalines high, so
  238.     out dx,al    ;open collector drivers not active.
  239.     mov dx,LPTControl    ;control register 2 (to set data)
  240.  
  241.     mov al,15h    ;SELIN=1 & STROBE=0 Enable IRQ7
  242.     out dx,ax    ;write data
  243.     call Stable    ;be sure to CLOCK it in.
  244.     pop dx
  245.     pop ax
  246.     ret
  247.  
  248. ; Set the control-lines to 'input' by setting all lines to high.
  249. ; (Open collector outputs not active).
  250. ;
  251. control_input:
  252.     push ax
  253.     push dx
  254.     mov dx,LPTData    ;data register
  255.     mov al,0ffh    ;d3=1, rest don't care
  256.     out dx,ax
  257.     mov dx,LPTControl ;Control register
  258.  
  259.     mov al,1ch    ;all outputs to 1, SELIN to 0 (INIT is not inverted!)
  260.     out dx,al    ;d3=1 & SELIN=0 -> write control
  261.     call Stable    ;be sure to CLOCK it in.
  262.     pop dx
  263.     pop ax
  264.     ret
  265.  
  266. ; read data from the cable, put it in AL
  267. read_data:        ;buffers must be in stable & data_input mode.
  268.     push bx
  269.     push dx
  270.     mov dx,LPTData    ;data register
  271.     xor al,al    ;bit 3 of AL will set line d3=0
  272.     out dx,al    ;all datalines to zero.
  273.     mov dx,LPTControl ;Control register
  274.  
  275.     mov al,1ch    ;SELIN=0, rest high (INIT is not inverted!).
  276.     out dx,al    ;d3=0 & SELIN=0 -> Read Data.
  277.     mov dx,LPTStatus ;status register
  278.     in al,dx    ;read it
  279.     mov bl,al    ;save higher bits of data to BL (AL will be changed)
  280.     mov dx,LPTControl    ;control register
  281.     in al,dx    ;read it (lower bits of data now in AL)
  282.     call stable    ;put all buffers in tri-state mode.
  283.     call data_input ;a bug, the change from RD->RC could activate WD.
  284.  
  285. ; Translation incomming data via control lines.
  286. ; (some lines are inverted, and I can't read 8 bits at once)
  287. ;
  288. ; Let's say, all datalines on the cable are high:
  289. ;
  290. ; databits: 7654 3210
  291. ; value:    1111 1111
  292. ;
  293. ; Then the input from the control registers would look like this:
  294. ;
  295. ; AL: data read from control register
  296. ;
  297. ; AL bits: 7654 3210  centronics  corresponding
  298. ; value:   XXXX X100   keyword:    databits:
  299. ;          |||| |||`- strobe         d0
  300. ;          |||| ||`-- auto LF        d1
  301. ;          |||| |`--- init           d2
  302. ;          |||| `---- invalid        x
  303. ;          ````------ invalid        x
  304. ;
  305. ; BL: data read from status register
  306. ;
  307. ; BL bits: 7654 3210  centronics  corresponding
  308. ; value:   0111 1XXX   keyword:     databits:
  309. ;          |||| |```- invalid          x
  310. ;          |||| `---- error           d3
  311. ;          |||`------ select          d4
  312. ;          ||`------- paper empty     d5
  313. ;          |`-------- ack             d6
  314. ;          `--------- busy            d7
  315. ;
  316. translate_data:
  317.     xor al,03h    ;invert bits 0 and 1
  318.     and al,07h    ;discards bits not needed in AL.
  319.     xor bl,80h    ;invert bit 7 in BL (busy).
  320.     and bl,0f8h    ;discard bits not needed in BL.
  321.     or al,bl    ;merge them together (high bits in BL, lower in AL)
  322.  
  323.     pop dx
  324.     pop bx
  325.     ret
  326.  
  327.  
  328.  
  329. ; Read the controlbits from the cable (busy, pout & sel)
  330. ;
  331. ; BL: control read from control register 1, represents the REAL line
  332. ;     status.
  333. ;
  334. ; BL bits: 7654 3210  centronics:    cable:  parnet:
  335. ; value:   0111 1XXX   
  336. ;          |||| |```- invalid          x
  337. ;          |||| `---- error            x
  338. ;          |||`------ select          busy    ack
  339. ;          ||`------- paper empty     pout    req
  340. ;          |`-------- ack             sel     ctl
  341. ;          `--------- busy             x
  342. ;
  343. ;Output: like BL above, but shifted 4 bits to the right.
  344. ;
  345. read_control:            ;control lines must be set to input first.
  346.     push ax
  347.     push cx
  348.     push dx
  349.     call Stable        ;Stable mode = Read_Control mode.
  350.     mov dx,LPTStatus    ;Status register
  351.     in al,dx        ;read it
  352.     mov cl,04        ;need CL to shift AL
  353.     shr al,cl        ;shift 4 bits to the right.
  354.     mov bl,al        ;save it to BL
  355.     and bl,07        ;discard bits not needed.
  356.     pop dx
  357.     pop cx
  358.     pop ax
  359.     ret            ;ready, controlbits in BL.
  360.  
  361. ;-----------------------------------------------------------------------------
  362. ; Clear the parnet ack-bit. Leave other control lines high.
  363. ; Warning: I can't read my own control-lines back!
  364. ;
  365. clear_ack_only:
  366.     push bx
  367.     push cx
  368.     mov bl,0feh        ; set parnet ack-bit to 0
  369.     call write_control    ; write control
  370.     pop cx
  371.     pop bx
  372.     ret
  373.  
  374. ;-----------------------------------------------------------------------------
  375. ; Set the parnet ack-bit. Leave other control lines high.
  376. ; Warning: I can't read my own control-lines back!
  377. ;
  378. Set_ack_all:
  379.     push bx
  380.     push cx
  381.     mov bl,0fh        ; set all bits to 1 (including ACK)
  382.     call write_control    ; write control
  383.     pop cx
  384.     pop bx
  385.     ret
  386.  
  387. ; Write control
  388. ;
  389. ; BL: controlbits to be written
  390. ;
  391. ; BL bits: 7654 3210                centronics
  392. ; value:   XXXX X100  controlbits:  keyword:
  393. ;          |||| |||`-    ack        strobe
  394. ;          |||| ||`--    req        auto LF
  395. ;          |||| |`---    ctl        init
  396. ;          |||| `----     x         selin
  397. ;          ````------     x         invalid
  398. ;
  399. ; !The value in BL represents the REAL LINE STATUS, so a bit=0 means the
  400. ; !line is on low voltage.
  401. ;
  402. ; The INIT output is on the parallel card not inverted. This bit will be
  403. ; inverted in this procedure, so you don't have to care about it.
  404. ;
  405. write_control:        ; Interface must be in stable mode!
  406.     push ax
  407.     push dx
  408.     mov dx,LPTData    ; data register
  409.     mov al,0ffh    ; d3=1, rest don't care.
  410.     out dx,al    ;
  411.     mov dx,LPTControl ; control register
  412.     mov al,bl    ; move control bits to AL (for OUT-instruction)
  413.     xor al,0bh    ; invert all lines except for INIT (=ctl)
  414.     or al,08h    ; SELIN = 0 (inverted!)
  415.     out dx,al    ; SELIN = 0 & d3=1 -> write control.
  416.     call Stable
  417.     pop dx
  418.     pop ax
  419.     ret
  420.  
  421. ; Write DATA
  422. ;
  423. ; AL: data to be written.
  424. ; WARNING: AL will be destroyed after write.
  425. ;
  426. write_data:            ; interface must be in stable mode!
  427.     push dx
  428.     mov dx,LPTData        ; data register
  429.     out dx,al        ; put it on (not on cable yet).
  430.     mov dx,LPTControl    ; control register
  431.     mov al,15h        ; SELIN=1, strobe=0, enable IRQ7
  432.     out dx,al        ; write data
  433.     call Stable        ; be sure to 'CLOCK' it in.
  434.     pop dx
  435.     ret
  436.  
  437.  
  438. ; (void) paraddress(int16 myaddr,int16 LPTAddress)
  439. ;
  440. ; Set my ParNet address (1-254) and LPT port address (0378h,03bch or 0278h)
  441. ;
  442. ; ParNet addresses 0 and 255 are reserved!
  443. ;
  444. paraddress    PROC
  445.         ARG myad:word,lptad:word
  446.  
  447.     mov ax,myad        ; my parnet address
  448.     mov ParNetAddr,al    ; store address
  449.     mov ax,lptad        ; LPT port address
  450.     mov LPTData,ax        ; Place data port address
  451.     inc ax            ; next register is status port
  452.     mov LPTStatus,ax
  453.     inc ax            ; next register is control port
  454.     mov LPTControl,ax
  455.     call data_input        ; data lines high
  456.     call control_input    ; control lines high
  457.     ret
  458.  
  459. paraddress     ENDP
  460.  
  461. ; int  = pardataready(void)
  462. ;
  463. ; Check for data present (e.g. after an IRQ7).
  464. ;
  465. ; Returns:  1 if packet is probably pending for you
  466. ;           0 if line is currently idle
  467. ;          -1 if packet isn't for you
  468. ;
  469. ; If line has been aquired but no control address has been
  470. ; put on it yet, pardataready() will wait for a control
  471. ; address.  Thus, after a signal, a single call to
  472. ; pardataready() should suffice.
  473. ;
  474. pardataready    PROC
  475.     call data_input        ; be sure all lines
  476.     call control_input    ; are set to input.    
  477. .pdstable:
  478.     call read_control    ; read control in BL
  479.     mov cl,bl        ; save it
  480.     call read_data        ; read data in AL
  481.     call read_control    ; read control in BL
  482.     cmp cl,bl
  483.     jne .pdstable
  484.  
  485.     ;   Now, pardataready might be called after the sending machine
  486.     ;   has aquired but before it can assert REQ.  However, the
  487.     ;   sending machine has already (guarenteed) placed its address
  488.     ;   on the data port.  So while the address matches, loop while
  489.     ;   REQ not asserted.
  490.  
  491.     test bl,02        ; ~REQ asserted?
  492.     jz .pd10        ; yes
  493.     cmp  [ParNetAddr],al    ; no, does data match anyway?
  494.     je .pdstable        ; YES, loop until get ~REQ or
  495.     jmp short .pdfail    ; data bad.
  496.  
  497. .pd10:
  498.     test bl,04        ; ~REQ is asserted, CTL=1 ?
  499.     jz .pdrn        ; no, middle of some packet.
  500.     cmp [ParNetAddr],al    ; yes, my address?
  501.     jne .pdrn        ; nope
  502.     mov ax,1        ; yes, packet (probably) for us.
  503.     jmp short .pdend
  504.  
  505. .pdfail:
  506.     test bl,04        ; fail due to ~REQ not asserted.
  507.     jz .pdrn        ; CTL=0, line busy
  508.     xor ax,ax        ; line idle.
  509.     jmp short .pdend
  510.  
  511. .pdrn:
  512.     mov ax,-1        ; line busy, packet not for me.
  513. .pdend:
  514.     ret
  515.  
  516. pardataready    ENDP
  517.  
  518. ;n = parreadV(unsigned char *buf,int16 bytes, buf2,bytes2, ..., NULL, NULL)
  519. ;n = parread(unsigned char *buf,int16 bytes)
  520. ;
  521. ;Read a pending packet.
  522. ;
  523. ;Returns: n = -1 (1 second timeout, no packet pending)
  524. ;       n = 0 to bytes-1 (1 second timeout after transmission interrupted)
  525. ;      n = bytes (success), or n > bytes (transmitting machine's packet
  526. ;          was larger than we can handle, extra bytes thrown out)
  527. ;
  528. ;NOTE:    Requesting an odd number of bytes is O.K. but if you request N where
  529. ;    N is odd and the writer sends N + 1 you will never know (N will be
  530. ;    returned). See also parwrite() below.
  531. ;
  532.  
  533. parreadV PROC
  534.      ARG buf:ptr, byts:word, nextbuf: ptr, nextbyts:word
  535.  
  536.          mov al,1
  537.          mov NextRVector,al      ; secondary buffer(s) present
  538.          jmp short parRbegin
  539.  
  540. parreadV ENDP
  541.  
  542. parread    PROC
  543.         ARG buf:ptr, byts:word, nbuf:ptr, nextbyts:word
  544.                                 ; ^these fake params are needed for reference.
  545.         mov al,0                ; no secondary buffer(s)
  546.         mov NextRVector,al
  547.  
  548. parRbegin:
  549.     if    @Datasize NE 0
  550.                 push ds         ; the 'USES' macro doesn't work here.
  551.                 push es
  552.         push si
  553.         push di
  554.                 les     di,buf  ; es:di = buf, my data segment needs to
  555.                                 ; stay intact.
  556.     else
  557.                 push ds
  558.                 push es
  559.         push si
  560.         push di
  561.                 mov ax,ds
  562.                 mov es,ax       ; to simulate a mov es,ds
  563.                 mov di,buf      ; es:di = buf (ds already set)
  564.     endif
  565.  
  566.     cld            ; all moves will be forward.
  567.     mov si,byts        ; maximum number of bytes to read.
  568.     call data_input        ; ensure all
  569.     call control_input    ; are inputs.
  570.     call Stable        ; ensure line not asserted.
  571.     mov DX,[ParLLTimeout]    ; DX = timeout load
  572.  
  573.     ;  Wait loop for address mark
  574.     ;  Ctl = 1, ~DReq = 0
  575. .rmstab:
  576.     call read_control    ; read control in BL
  577.     mov cl,bl        ; save it
  578.     call read_data        ; read data in AL
  579.     call read_control    ; read control in BL
  580.     cmp cl,bl        ; control lines stable?
  581.     jne .rmstab        ; nope
  582.     test bl,04        ; expect CTL=1
  583.     jz .rms1        ; nope
  584.     test bl,02        ; expect ~REQ=0
  585.     jz .rms2        ; yes
  586.  
  587. .rms1:
  588.     dec dx            ; decrement timeout count
  589.     jnz .rmstab        ; no timeout.
  590.     mov ax,-1        ; return value: read timeout
  591.     jmp .rmend        ; no address mark!
  592.  
  593. .rms2:
  594.     cmp [ParNetAddr],al    ; My address?
  595.     jne .rms1        ; no, timeout loop
  596.  
  597.     ; Got my address, ~Ack byte.
  598.  
  599.     mov DX,[ParLLTimeout]    ; reset timeout
  600.     call Clear_Ack_Only    ; set ~Ack to 0
  601.  
  602. .rms4:  call read_control    ; get control
  603.     test bl,02        ; wait for ~REQ to go away
  604.     jnz .rms5
  605.     dec dx            ; decrement timeout count
  606.     jnz .rms4
  607.     mov ax,-2        ;~REQ not released?
  608.     jmp .rmend
  609.  
  610. .rms5:  call Set_Ack_All    ; release ~ACK
  611.  
  612.     mov cx,0        ; set # of bytes read to 0
  613.     jmp .rms10        ; skip past move
  614.  
  615.  
  616.     ; MAIN READ LOOP
  617.     ;
  618.     ; cx holds count (number of bytes read).
  619.     ; DI buffer pointer (place to store data).
  620.  
  621. .rms10loop:
  622. ;    mov es:[di],al        ; store data
  623. ;    inc di            ; next address
  624.     stosb
  625.  
  626. .rms10:
  627.     call Read_Control
  628.     test bl,02        ; wait for ~REQ asserted
  629.     jz .rms20
  630.     call Read_control
  631.     test bl,02        ; again
  632.     jz .rms20
  633.     mov DX,[ParLLTimeout]    ; reset timeout
  634.  
  635. .rms11: call Read_Control
  636.     test bl,02        ; wait for ~REQ asserted with timeout
  637.     jz .rms20
  638.     dec dx            ; decrement timeout count
  639.     jnz .rms11
  640.     mov ax,-1
  641.     jmp short .rmend    ; timeout
  642.  
  643. .rms20: call read_data        ; get data in al and
  644.     call Clear_Ack_Only    ; assert ~ACK
  645.  
  646.     ; note on CTL = 1 end sequence this data item is a dummy
  647.  
  648. ;    mov es:[di],al          ; store data
  649. ;    inc di            ; next address
  650.     stosb
  651.  
  652.     inc cx            ; optimized, but not quite true,
  653.     inc cx            ; we've only written one 1 sf.
  654.  
  655.     call Read_Control
  656.     test bl,02        ; wait for ~REQ released
  657.     jnz .rms30
  658.     call Read_Control
  659.     test bl,02        ; again
  660.     jnz .rms30
  661.     mov DX,[ParLLTimeout]    ; reset timeout
  662. .rms21:
  663.     call Read_Control
  664.     test bl,02        ; wait for ~REQ released with timeout
  665.     jnz .rms30
  666.     dec dx            ; decrement timeout count
  667.     jnz .rms21        ; no timeout yet?
  668.     jmp short .rmendsub    ; sub because CX is 2 ahead
  669.  
  670.  
  671. .rms30:
  672.     call read_data        ; get data in al
  673.     call Read_Control    ; get CTL status in BL
  674.     call Set_Ack_All    ; release ~ACK    
  675.     test bl,04        ; EOP if CTL=1
  676.     jnz .rmeop
  677.  
  678.     ; CANNOT STORE DATA HERE! In case odd # bytes requested,
  679.     ; second byte would overflow buffer (each handshake sequence ALWAYS
  680.     ; transfers 2 bytes of information)
  681.  
  682.     dec  si            ; # of bytes remaining
  683.     jz   .rmodd        ; already zero, # of bytes were odd.
  684.     dec  si
  685.  
  686.     jz  .rmste        ; reached zero (even bytes)
  687.     jmp .rms10loop        ; continue if >0.
  688.  
  689. .rmodd: dec si            ; si must be -1 when odd # bytes requested.
  690.     dec cx            ; fixup count (was one ahead)
  691.     jmp short .rmeven
  692.  
  693. .rmste:
  694.         mov es:[di],al        ; if si = 0, its's even and we
  695.                 ; should store the last byte.
  696. .rmeven:
  697.         cmp NextRVector,0        ; if next buffer does not exist
  698.         je  .rmovflow           ; overflow
  699. .rmsev0:
  700.         cmp WORD PTR nbuf,0     ; next buffer points to somewhere?
  701.        if @Datasize NE 0
  702.         jne .rmsev1
  703.         cmp WORD PTR nbuf+2,0   ; check also the segment
  704.        else
  705.         je .rmovflow
  706.        endif
  707. .rmsev1:
  708.        mov si,nextbyts          ; length of next buffer
  709.        if @Datasize NE 0
  710.         les di,nbuf             ; load pointer to next buffer
  711.         add bp,6                ; this is VERY tricky  = sizeof(nbuf+nbyts)
  712.        else
  713.         mov di,nbuf             ; load pointer to next buffer
  714.         add bp,4                ; this one is also VERY tricky
  715.        endif
  716.         or si,si                ; size of this buffer = 0 ?
  717.         jz .rmsev0              ; yes, then check for next buffer.
  718.         jmp .rms10              ; proceed reading
  719.  
  720. .rmovflow:
  721.         if @Datasize NE 0
  722.          mov di,ds
  723.          mov es,di              ; to simulate move es,ds
  724.         endif
  725.  
  726.     mov di,OFFSET Dummybuf    ; overflow, use dummy buffer
  727.  
  728.     jmp .rms10
  729.  
  730. .rmeop:
  731.     cmp al,0        ; EOP data better be 0!
  732.     je .rmendsub
  733.     mov ax,-3        ; bad protocol
  734.     jmp short .rmend
  735.  
  736. .rmendsub:
  737.     dec cx            ; because we were two ahead
  738.     dec cx
  739. .rmend:
  740.     call data_input
  741.     call Set_Ack_All    ; setting ~ACK to input
  742.     call Stable
  743.     pop di
  744.     pop si
  745.         pop es                  ; normally the 'USES' macro inserts these.
  746.         pop ds
  747.     ret            ; return value in AL
  748.  
  749. parread    ENDP
  750.  
  751. ;n = parwriteV(int 16 destaddr, unsigned char *buf, int16 bytes,
  752. ;              buf2, bytes2, ..., NULL, NULL)
  753. ;n = parwrite(int16 destaddr, unsigned char *buf, int16 bytes)
  754. ;
  755. ;Write a packet.
  756. ;
  757. ;Returns: n = -2    Cannot write anything, a packet is pending
  758. ;            (instantanious)
  759. ;      n = -1    Destination machine does not respond (1 sec timeout)
  760. ;      n = N        N bytes written ok (success if n == bytes)
  761. ;
  762. ; NOTE: sending an odd number of bytes is O.K. but if you write N where
  763. ;    N is odd and the reader requests N + 1 he will get N + 1 the last
  764. ;    byte being garbage.
  765. ;
  766. parwriteV PROC
  767.       ARG dest:word, buf:ptr, byts:word, nextbuf:ptr, nextbyts:word
  768.  
  769.       mov al,1
  770.       mov NextWVector,al
  771.       jmp short parWbegin
  772.  
  773. parwriteV ENDP
  774.  
  775. parwrite  PROC
  776.       ARG dest:word, buf:ptr, byts:word, nbuf:ptr, nextbyts:word
  777.                 ;         ^these fake params are needed for
  778.                 ;          reference.
  779.       mov al,0
  780.       mov NextWVector,al    ; no secondary buffer(s)
  781.  
  782. parWbegin:
  783.     if @Datasize NE 0
  784.         push ds        ; the 'USES' macro doesn't work here.
  785.         push es
  786.         push si
  787.         push di
  788.                 les si,buf      ; es:si = source-buffer to be written
  789.     else
  790.                 push ds
  791.         push es
  792.         push si
  793.         push di
  794.                 mov ax,ds
  795.                 mov es,ax       ; to simulate a mov es,ds
  796.                 mov si,buf      ; es:si = buf (ds already set)
  797.     endif
  798.  
  799.     cld            ; all moves will be forward.
  800.     mov ax,dest        ; destination address.
  801.     mov DestAddress,al    ; can't do a direct move to DestAddress.
  802.     mov di,byts        ; number of bytes to write.
  803.  
  804.  
  805.     call data_input        ; ensure all
  806.     call control_input    ; are inputs.
  807.     call Stable        ; ensure line not asserted.
  808.     mov DX,[ParLLTimeout]    ; DX = timeout load
  809.  
  810. .wmstab:
  811.     cli            ; disable interrupts
  812.     call Read_Control
  813.     mov cl,bl
  814.     call read_data    
  815.     call Read_Control    ; get stable control
  816.     cmp cl,bl
  817.     je .wmstab1
  818.     sti            ; set interrupts
  819.     jmp short .wmstab
  820.  
  821.     ; Interrupts still disabled
  822.     ; BL holds ~ACK ~REQ and CTL status
  823.  
  824. .wmstab1:
  825.     mov ax,-2        ; number of bytes written yet (=none).
  826.     cmp bl,07h        ; ~ACK=1, ~REQ=1, CTL=1 ?
  827.     je .wm02
  828.  
  829.     ; if CTL=1, ~REQ=0 and AL=my address then
  830.     ; return with -2
  831.  
  832.     test bl,02h        ; ~REQ = 0?
  833.     jne .wm01        ; nope
  834.     test bl,04h        ; CTL = 1?
  835.     je .wm01        ; nope
  836.     cmp [ParNetAddr],al    ; somebody is calling me?
  837.     jne .wm01        ; nope
  838.  
  839.     sti            ; enable interrupts
  840.     jmp .wmend
  841.  
  842. .wm01:
  843.     sti            ; enable interrupts
  844.     dec dx            ; decrement timeout count
  845.     jnz .wmstab
  846.     jmp .wmend
  847.  
  848.     ; interrupts still disabled
  849.     ; we almost own the line
  850.  
  851. .wm02:
  852.     call Read_Control
  853.     call Clear_Ack_Only    ; assert ~ACK
  854.     test bl,01
  855.     jne .wm05        ; ACK was released before, have line!
  856.  
  857.     call Set_Ack_All    ; release ~ACK
  858.     jmp short .wm01
  859.  
  860.     ; Line now aquired.
  861.  
  862. .wm05:
  863.     sti            ; enable interrupts
  864.     mov al,DestAddress
  865.     call Write_Data        ; set destination address on datalines.
  866.  
  867.     ; Before asserting ~REQ, pulse CTL to cause interrupt on remote
  868.     ; machines. Note that our address is already on the datalines.
  869.  
  870.     mov bl,02h        ; leave ~ACK=0 and ~REQ=1, but CTL->0
  871.     call Write_Control
  872.     mov bl,06h        ; leave ~ACK=0 and ~REQ=1, but CTL->1
  873.     call Write_Control
  874.     mov bl,04h        ; leave ~ACK=0 and CTL=1, but ~REQ->0  
  875.     call Write_Control    ; assert REQ
  876.     mov bl,05h        ; leave CTL=1 and ~REQ=0, but ~ACK->1
  877.     call Write_Control    ; release ACK
  878.                 ; (note that REQ->0 before ACK->released)
  879.  
  880.     mov ax,-1        ; number of bytes written yet (none).
  881.  
  882.     ; interrupts enabled for transfer (fully handshaked)
  883.     ;
  884.     ; Address mark ~ACK, wait for ~ACK asserted.
  885. .wm10:
  886.     call Read_Control
  887.     test bl,01        ; ~ACK asserted?
  888.     jz .wm15        ; yes, remote machine got my address mark
  889.     mov DX,[ParLLTimeout]    ; DX = timeout count
  890. .wm11:
  891.     call Read_Control
  892.     test bl,01
  893.     jz .wm15
  894.     dec dx            ; decrement timeout count
  895.     jnz .wm11        ; timeout?
  896.     jmp .wmend        ; yes.
  897.  
  898.     ; got ack, now set CTL = 0 (leaves at least one line 0 so
  899.     ; nobody else thinks the bus is idle!)
  900.     ;
  901.     ; note:  Since this is the address mark, and is sampled by
  902.     ;     the reader before it asserts ~ACK, I can set CTL
  903.     ;     = 0 now instead of waiting till after ~ACK is
  904.     ;     released.
  905.  
  906. .wm15:
  907.     mov bl,01h        ; leave ACK=1, REQ=0, but CTL->0
  908.     call Write_Control    ; set CTL = 0 for duration of packet
  909.     mov bl,03h        ; leave ACK=1, CTL=0, but REQ->1
  910.     call Write_Control    ; release ~REQ
  911.  
  912.     mov cx,0        ; number of bytes written (none).
  913.  
  914.     ; Data transfer loop
  915.     ;
  916.     ; wait for ~ACK to be released (-> 1). If no more bytes to
  917.     ; write, then skip to .wm50
  918.  
  919. .wm20:
  920.     or  di,di        ; more data in this buffer?
  921.     jz .wm50        ; nope
  922.  
  923.     call Read_Control
  924.     test bl,01        ; wait for ~ACK to be released
  925.     jnz .wm30
  926.     mov DX,[ParLLTimeout]    ; DX = timeout countdown
  927. .wm21:
  928.     call Read_Control
  929.     test bl,01
  930.     jnz .wm30        ; need the timeout here?
  931.     call Read_control
  932.     test bl,01        ; check ACK again
  933.     jnz .wm30
  934.     dec dx
  935.     jnz .wm21
  936.     jmp .wmend        ; timeout
  937.  
  938.     ; Assert ~REQ for this data byte and wait for ~ACK
  939.  
  940. .wm30:
  941.         mov al,es:[si]        ; get next data byte.
  942.     inc si            ; this can't be done with a LODSB :-(
  943.  
  944.     call Write_Data        ; store data and ..
  945.     mov bl,01h        ; leave ACK=1, CTL=0, but REQ->0
  946.     call Write_Control    ; .. assert ~REQ
  947.  
  948.         mov al,es:[si]          ; get next data bytes
  949.     inc si            ; this can't be done with a LODSB :-(
  950.  
  951.     inc cx            ; number of bytes written (this only)
  952.                 ; (not valid until we get ACK which
  953.                 ;  is why the wmendsub is included)
  954.  
  955.     call Read_Control
  956.     test bl,01        ; wait for ACK asserted
  957.     jz .wm40
  958.     call Read_Control
  959.     test bl,01        ; look again
  960.     jz .wm40
  961.     mov DX,[ParLLTimeout]    ; DX = timeout count
  962. .wm31:
  963.     call Read_Control
  964.     test bl,01        ; wait for ACK asserted with timeout
  965.     jz .wm40
  966.     dec dx            ; decrement timeout count
  967.     jnz .wm31
  968.     jmp short .wmendsub    ; timeout, no bytes written!
  969.  
  970.     ; have ~ACK, so byte transmitted. increment bytes written,
  971.     ; decr. of bytes left was already done before.
  972.     ; now send second byte and loop back.
  973.  
  974. .wm40:    call Write_Data        ; data was loaded in AL before.
  975.     mov bl,03h        ; leave ACK=1, CTL=0, but REQ->1
  976.     call Write_Control    ; release ~REQ
  977.     inc cx            ; increment number of bytes written
  978.  
  979.     dec di            ; one less bytes
  980.     jz .wm40a        ; odd number of bytes were written
  981.     dec di            ; one less bytes
  982.     jz .wm50        ; these were the last even bytes(s)
  983.  
  984.     jmp .wm20        ; loop back
  985.  
  986. .wm40a: dec di            ; fixup, di = -1 when odd bytes requested
  987.     jmp short .wm50        ; last odd byte, go send an EOP.
  988.  
  989.     ; Last byte in buffer has been transmitted.
  990.     ;
  991.     ; Get next buffer in vector.
  992.  
  993. .wm50:    cmp NextWVector,0    ; if next buffer does not exist
  994.     je .wm50a        ; yes.
  995.         cmp WORD PTR nbuf,0     ; next buffer points to somewhere?
  996.        if @Datasize NE 0
  997.         jne .wmnext
  998.         cmp WORD PTR nbuf+2,0   ; check also the segment
  999.        endif
  1000.         je .wm50a        ; is zero, so no next buffer.
  1001. .wmnext:
  1002.        mov di,nextbyts          ; length of next buffer
  1003.        if @Datasize NE 0
  1004.         les si,nbuf             ; load pointer to next buffer
  1005.         add bp,6                ; this is VERY tricky  = sizeof(nbuf+nbyts)
  1006.        else
  1007.         mov si,nbuf             ; load pointer to next buffer
  1008.         add bp,4                ; this one is also VERY tricky
  1009.        endif
  1010.     jmp .wm20        ; proceed write.
  1011.  
  1012. .wm50a:
  1013.     ;   Last byte has been transmitted,
  1014.     ;
  1015.     ;   Wait for ~ACK to be released and then assert ~REQ with
  1016.     ;   EOP & CTL = 1
  1017.     ;
  1018.     ;   (timing on read is that CTL is sampled when ~REQ is
  1019.     ;   RELEASED so no timing window here)
  1020.  
  1021.     call Read_Control    ; Wait ~ACK released
  1022.     test bl,01
  1023.     jz .wm50
  1024.  
  1025.     mov al,0
  1026.     call Write_Data        ; EOP mark (=0)
  1027.     mov bl,01h        ; leave ACK=1 and CTL=0, but REQ->0
  1028.     call Write_Control    ; assert ~REQ
  1029.  
  1030.     ; Wait for ~ACK asserted
  1031.  
  1032.     call Read_Control
  1033.     test bl,01        ; ACK asserted?
  1034.     jz .wm60        ; yes
  1035.     mov DX,[ParLLTimeout]    ; DX = timeout count
  1036. .wm51:
  1037.     call Read_Control
  1038.     test bl,01        ; wait for ACK asserted with timeout
  1039.     jz .wm60
  1040.     dec dx            ; decrement timeout count
  1041.     jnz .wm51
  1042.     mov ax,-3        ; EOP failed
  1043.     jmp short .wmend
  1044.  
  1045.     ; Set CTL=1 then release ~REQ, then wait for ~ACK released
  1046. .wm60:
  1047.     mov bl,05h        ; leave ACK=1, REQ=0, but CTL->1
  1048.     call Write_Control    ; release CTL
  1049.     mov bl,07h        ; leave ACK=1, CTL=1, but REQ->1
  1050.     call Write_Control    ; release ~REQ
  1051.     
  1052.     ; Wait ~ACK released ?
  1053.  
  1054. .wm61:    call Read_Control
  1055.     test bl,01
  1056.     jz .wm61
  1057.  
  1058.     ; Add DI to CX. This handles fixup if an odd number of bytes were
  1059.     ; requested written, DI will be -1 (odd) or 0 (even) and CX will 
  1060.     ; be one too large (odd) or perfect (even)
  1061.  
  1062.     add cx,di
  1063.     jmp short .wmend1
  1064.  
  1065. .wmendsub:
  1066.     dec cx            ; was ahead in count
  1067. .wmend1:
  1068.     mov ax,cx        ; return value in AX
  1069. .wmend:
  1070.     call data_input        ; set data port to input
  1071.     call control_input    ; set control lines to input
  1072.     pop di
  1073.     pop si
  1074.     pop es
  1075.     pop ds            ; normally done by the 'USES' macro
  1076.     ret            ; return value in AX
  1077.  
  1078. parwrite    ENDP
  1079.  
  1080.     END
  1081.